import os
import glob
import fnmatch
import argparse

from prettytable import PrettyTable, MARKDOWN

COLOR_MAP = {
    'grey'   : '\033[30m',
    'red'    : '\033[31m',
    'green'  : '\033[32m',
    'yellow' : '\033[33m',
    'blue'   : '\033[34m',
    'magenta': '\033[35m',
    'cyan'   : '\033[36m',
}
def colored(s, color=None):
    if not color:
        color = 'yellow'

    color = COLOR_MAP[color]

    return color + '{}\033[0m'.format(s)

def get_options_by_command_line():
    global OPTIONS

    arg_parser = argparse.ArgumentParser(description='CODECOUNT.md generation tool')

    args = vars(arg_parser.parse_args())
    args = dict(filter(lambda x: x[1] is not None, args.items()))

    OPTIONS = args

INCLUDES = [
    '**/*.*',
]
EXCLUDES = [
    # Folders
    'client/dist/**',
    'client/node_modules/**',
    'client/src/theme/**',
    'node_modules/**',
    'server/statics/libs/**',
    'server/doc/**',

    # Files
    'tz-abbr.yaml',
    'OPENSOURCE.md',
    'OPENSOURCE.cache.json',
    'CODELINES.md',
    'package-lock.json',
    'client/package-lock.json',

    # Pattern
    'db/**.sql',
    '**/*.zht.yaml',
    '**/*.min.css',
]

OUTPUT_FILE_NAME = 'CODELINES.md'

def output_markdown(code_lines):
    md_lines = [
        f'> Generated by `{os.path.relpath(__file__)}`\n',
        '# Code Lines\n',
        'Code line count in DataFlux Func project.\n',
    ]

    # Total
    md_lines.append(f"## Total\n")

    table = PrettyTable()
    table.align = 'r'
    table.set_style(MARKDOWN)
    table.field_names = [ 'Total Lines' ]
    table.add_row([ f"{code_lines['total']:,}" ])

    table_str = table.get_string() + '\n'
    md_lines.append(table_str)

    # By file type
    md_lines.append(f"## By Type\n")

    table = PrettyTable()
    table.set_style(MARKDOWN)
    table.field_names = [ 'Type', 'Lines' ]
    table.align['Type']  = 'l'
    table.align['Lines'] = 'r'
    for row in sorted(code_lines['byType'], key=lambda row: row[1], reverse=True):
        table.add_row([ f"`{row[0]}`", f"{row[1]:,}" ])

    table_str = table.get_string() + '\n'
    md_lines.append(table_str)

    # By folder
    md_lines.append(f"## By Folder\n")

    table = PrettyTable()
    table.set_style(MARKDOWN)
    table.field_names = [ 'Folder', 'Lines' ]
    table.align['Folder'] = 'l'
    table.align['Lines'] = 'r'

    for row in sorted(code_lines['byFolder'], key=lambda row: row[1], reverse=True):
        table.add_row([ f"`{row[0]}`", f"{row[1]:,}" ])

    table_str = table.get_string() + '\n'
    md_lines.append(table_str)

    # By file
    md_lines.append(f"## By File\n")

    table = PrettyTable()
    table.set_style(MARKDOWN)
    table.field_names = [ 'File', 'Lines' ]
    table.align['File'] = 'l'
    table.align['Lines'] = 'r'

    for row in sorted(code_lines['byFile'], key=lambda row: row[1], reverse=True):
        table.add_row([ f"`{row[0]}`", f"{row[1]:,}" ])

    table_str = table.get_string() + '\n'
    md_lines.append(table_str)

    # Gen md file
    md = '\n'.join(md_lines)
    with open(OUTPUT_FILE_NAME, 'w') as _f:
        _f.write(md)

def get_code_lines():
    code_lines = {
        'total'   : 0,
        'byType'  : [],
        'byFolder': [],
        'byFile'  : [],
    }

    type_code_lines_map   = {}
    folder_code_lines_map = {}
    for include in INCLUDES:
        for filename in glob.iglob(include, recursive=True):
            for exclude in EXCLUDES:
                if fnmatch.fnmatch(filename, exclude):
                    break

            else:
                if os.path.isfile(filename):
                    with open(filename, 'r') as _f:
                        try:
                            lines = len(_f.readlines())
                        except UnicodeDecodeError as e:
                            pass
                        else:
                            # Total lines
                            code_lines['total'] += lines

                            # Count by code type
                            file_type = filename.split('.').pop()
                            if file_type not in type_code_lines_map:
                                type_code_lines_map[file_type] = 0

                            type_code_lines_map[file_type] += lines

                            # Count by folder
                            folder = '.'
                            if '/' in filename:
                                folder = filename.split('/')[0]

                            if folder not in folder_code_lines_map:
                                folder_code_lines_map[folder] = 0

                            folder_code_lines_map[folder] += lines

                            # Count by file
                            code_lines['byFile'].append([ filename, lines ])

    code_lines['byType']    = [ [ ext,    lines ] for ext,    lines in type_code_lines_map.items() ]
    code_lines['byFolder'] = [ [ folder, lines ] for folder, lines in folder_code_lines_map.items() ]

    return code_lines

def main():
    get_options_by_command_line()

    code_lines = get_code_lines()

    output_markdown(code_lines)

if __name__ == '__main__':
    print(colored('Gen CODELINES.md', 'green'))
    main()
    print(colored('Done', 'green'))
